package cn.com.cifi.mars.bean.bo.returnCoefficient;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.apache.commons.collections4.MapUtils;

import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;

import cn.com.cifi.mars.bean.dto.MymContractDto;
import cn.com.cifi.mars.bean.dto.VsMarsFeeDto;
import cn.com.cifi.mars.constant.ActivationEnum;
import cn.com.cifi.mars.entity.SignPlanVersionSupplyDetail;
import cn.com.cifi.mars.entity.VsMarsGetinzcl;
import cn.com.cifi.mars.util.BigDecimalUtils;
import cn.com.cifi.mars.util.DateUtils;
import cn.com.cifi.mars.util.LocalDateTimeUtils;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

/**
 * 回款系数明源销售数据
 * @author: JingYuanXiang
 * @date: 2020/11/12 15:08
 */
@Data
public class ReturnCoefficientMySaleDataBO {

    @ApiModelProperty(name = "supplyDetailList", value = "供货详情列表")
    List<SignPlanVersionSupplyDetail> supplyDetailList;
    @ApiModelProperty(name = "contractList", value = "合同列表")
    List<MymContractDto> contractList;
    @ApiModelProperty(name = "contractList", value = "应收列表")
    List<VsMarsFeeDto> receivableList;
    @ApiModelProperty(name = "contractList", value = "实收列表")
    List<VsMarsGetinzcl> receiptsList;
    @ApiModelProperty(name = "tolerance", value = "容差")
    BigDecimal tolerance;

    @ApiModelProperty(name = "contractGUIDMap", value = "合同id映射关系")
    Map<String, String> contractGUIDMap;
    @ApiModelProperty(name = "receivableAmountMap", value = "获取应收金额映射(<key:合同id,<key:收款时间,value:金额>>)")
    Map<String, Map<String, BigDecimal>> receivableAmountMap;
    @ApiModelProperty(name = "receiptsAmountMap", value = "获取实收金额映射(<key:合同id,<key:收款时间,value:金额>>)")
    Map<String, Map<String, BigDecimal>> receiptsAmountMap;


    //初始化Map
    public void initializeMap(){
        if(CollectionUtils.isNotEmpty(contractList)){
            //获取交易id月合同id映射关系
            contractGUIDMap = contractList.stream().collect(Collectors.toMap(MymContractDto::getTradeGUID, MymContractDto::getContractGUID));
            //补充退房应收数据
            contractList.stream()
                    .filter(s -> null != s.getCloseDate() && LocalDateTimeUtils.compareDate(s.getCloseDate(), LocalDateTime.now(), 0))
                    .forEach(s->{
                        if(ActivationEnum.CLOSE.getKey().equals(s.getStatus())){
                            receivableList.add(new VsMarsFeeDto(s.getContractGUID(),s.getCloseDate(),BigDecimal.ZERO.subtract(s.getCjRmbTotal())));
                        }
            });
        }
        if(CollectionUtils.isNotEmpty(receivableList)) {
            //获取应收金额映射(<key:合同id,<key:收款时间,value:金额>>)
            receivableAmountMap = receivableList.stream()
                    .collect(Collectors.groupingBy(VsMarsFeeDto::getContractGUID,
                            Collectors.toMap(s -> LocalDateTimeUtils.formatTime(s.getReceiptDate(), DateUtils.DATE_JFP_STR), VsMarsFeeDto::getReceiptAmount, BigDecimal::add)));

        }
        if(CollectionUtils.isNotEmpty(receiptsList)){
            //获取实收金额映射(<key:合同id,<key:收款时间,value:金额>>)
            receiptsAmountMap = receiptsList.stream()
                    .filter(s -> contractGUIDMap.get(s.getSaleGUID()) != null)
                    .collect(Collectors.groupingBy(s -> contractGUIDMap.get(s.getSaleGUID()),
                            Collectors.toMap(s -> LocalDateTimeUtils.formatTime(s.getGetDate(), DateUtils.DATE_JFP_STR),
                                    s -> s.getRmbAmount() == null ? BigDecimal.ZERO : s.getRmbAmount(), BigDecimal::add)));
        }

        if(MapUtils.isEmpty(receivableAmountMap)){
            this.receivableAmountMap = new HashMap<>();
        }
        if(MapUtils.isEmpty(receiptsAmountMap)){
            this.receiptsAmountMap = new HashMap<>();
        }

    }

    /**
     * 获取合同应收金额
     * @param contractGUID 合同id
     * @return
     */
    public BigDecimal getReceivableAmountByContractGUID(String contractGUID) {
        BigDecimal amount = BigDecimal.ZERO;
        if(CollectionUtils.isNotEmpty(receivableAmountMap.get(contractGUID))){
            amount = receivableAmountMap.get(contractGUID).entrySet().stream().map(Map.Entry::getValue).reduce(BigDecimal.ZERO, BigDecimal::add);
        }
        return amount;
    }

    /**
     * 获取小于指定年月的合同应收金额
     * @param contractGUID 合同id
     * @return
     */
    public BigDecimal getReceivableAmountByContractGUIDLeYearMonth(String contractGUID, String yearMonth) {
        BigDecimal amount = BigDecimal.ZERO;
        if(CollectionUtils.isNotEmpty(receivableAmountMap.get(contractGUID))){
            amount = receivableAmountMap.get(contractGUID).entrySet().stream()
                    .filter(s->compareYearMonth(s.getKey(),yearMonth) < 0)
                    .map(Map.Entry::getValue).reduce(BigDecimal.ZERO, BigDecimal::add);
        }
        return amount;
    }


    /**
     * 获取大于等于指定年月的合同应收收金额映射
     * @param contractGUID
     * @return
     */
    public Map<String, BigDecimal> getContractReceiptsAmountMapByContractGUIDGeYearMonth(String contractGUID, String yearMonth) {
        return receivableAmountMap.get(contractGUID).entrySet().stream()
                .filter(s->compareYearMonth(s.getKey(),yearMonth) >= 0)
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
    }



    /**
     * 获取合同实收金额
     * @param contractGUID
     * @return
     */
    public BigDecimal getReceiptsAmountByContractGUID(String contractGUID) {
        BigDecimal amount = BigDecimal.ZERO;
        if(CollectionUtils.isNotEmpty(receiptsAmountMap.get(contractGUID))){
            amount = receiptsAmountMap.get(contractGUID).entrySet().stream().map(Map.Entry::getValue).reduce(BigDecimal.ZERO, BigDecimal::add);
        }
        return amount;
    }

    /**
     * 获取合同实收金额映射
     * @param contractGUID
     * @return
     */
    public Map<String, BigDecimal> getContractReceiptsAmountMapByContractGUID(String contractGUID) {
        return receiptsAmountMap.get(contractGUID);
    }

    /**
     * 获取合同id列表Map（key：标准业态编码，value：合同id列表）
     * @return
     */
    public Map<String, List<String>> getContractGUIDListByProProductCodeMap() {
        Map<String, List<String>> contractGUIDListByBldPrdIDMap = contractList.stream()
                .collect(Collectors.groupingBy(MymContractDto::getBldPrdID,
                        Collectors.mapping(MymContractDto::getContractGUID, Collectors.toList())));

        Map<String, List<String>> contractGUIDListByProProductCodeMap = supplyDetailList.stream()
                .filter(s -> contractGUIDListByBldPrdIDMap.get(s.getOrgId()) != null)
                .collect(Collectors.toMap(SignPlanVersionSupplyDetail::getProProductCode,
                        s -> contractGUIDListByBldPrdIDMap.get(s.getOrgId()), (s1, s2) -> {
                            s1.addAll(s2);
                            return s1;
                        }));
        return contractGUIDListByProProductCodeMap;
    }

    /**
     * 获取标准业态首次签约日期Map(key:标准业态编码，value：首次签约日期)
     * @param contractGUIDListMap
     * @param mySaleDataBO
     * @return
     */
    public Map<String, LocalDateTime> getProProductSignFirstTimeMap(Map<String, List<String>> contractGUIDListMap, ReturnCoefficientMySaleDataBO mySaleDataBO) {
        //获取合同签约时间Map(key:合同id,value:签约时间)
        Map<String,LocalDateTime> ywgsDateMap = contractList.stream().filter(s->s.getStatus().equals(ActivationEnum.ACTIVATION.getKey()))
                .collect(Collectors.toMap(MymContractDto::getContractGUID, MymContractDto::getYwgsDate));

        Map<String, LocalDateTime> proProductSignFirstTimeMap = contractGUIDListMap.entrySet().stream()
                .collect(Collectors.toMap(s -> s.getKey(), s -> s.getValue().stream()
                        .filter(s1->ywgsDateMap.get(s1) != null)
                        .map(s1 -> ywgsDateMap.get(s1)).min(Comparator.comparing(s1 -> s1)).orElse(null)));
        return proProductSignFirstTimeMap;
    }


    /**
     * 获取标准业态已售套数Map(key:标准业态编码,value:已售套数)
     * @return
     */
    public Map<String, BigDecimal> getProProductSoldNumMap() {
        Map<String, List<MymContractDto>> contractByBldPrdIDMap = contractList.stream()
                .filter(s -> s.getStatus().equals(ActivationEnum.ACTIVATION.getKey()))
                .collect(Collectors.groupingBy(MymContractDto::getBldPrdID));

        Map<String, BigDecimal> proProductSoldNumMap = supplyDetailList.stream()
                .filter(s->CollectionUtils.isNotEmpty(contractByBldPrdIDMap.get(s.getOrgId())))
                .collect(Collectors.toMap(SignPlanVersionSupplyDetail::getProProductCode,
                        s -> new BigDecimal(contractByBldPrdIDMap.get(s.getOrgId()).size()), BigDecimal::add));

        return proProductSoldNumMap;
    }

    /**
     * 获取标准业态供货套数Map(key:标准业态编码,value:供货套数)
     * @return
     */
    public Map<String, BigDecimal> getProProductSupplyNumMap() {
        Map<String, BigDecimal> proProductSupplyNumMap = supplyDetailList.stream()
                .collect(Collectors.toMap(SignPlanVersionSupplyDetail::getProProductCode,
                        s -> new BigDecimal(s.getRoomNumActual()+s.getParkingNumActual()), BigDecimal::add));

        return proProductSupplyNumMap;
    }


    /**
     * 获取标准业态回款率Map(key:标准业态，value：累计签约回款)
     * @return
     */
    public Map<String, BigDecimal> getProProductReturmRateMap() {
        //获取合同id列表Map（key：标准业态编码，value：合同id列表）
        Map<String, List<String>> contractGUIDListByProProductCodeMap = getContractGUIDListByProProductCodeMap();
        //获取标准业态签约金额Map
        Map<String, BigDecimal> proProductContractAmountMap = contractGUIDListByProProductCodeMap.entrySet().stream()
                .collect(Collectors.toMap(Map.Entry::getKey, s -> {
                    List<String> contractGUIDList = s.getValue();
                    return contractList.stream()
                            .filter(s1 -> contractGUIDList.contains(s1.getContractGUID()))
                            //保留 (激活状态 && 业绩归属时间小于当月的数据) || (关闭状态 && 业绩归属时间小于当月的数据 && 退房时间大于等于当月的数据)
                            .filter(s1 -> (
                                    s1.getStatus().equals(ActivationEnum.ACTIVATION.getKey())
                                    &&  LocalDateTimeUtils.compareDate(s1.getYwgsDate(), LocalDateTime.now(), 0)
                            ) || (
                                    s1.getStatus().equals(ActivationEnum.CLOSE.getKey())
                                    &&  LocalDateTimeUtils.compareDate(s1.getYwgsDate(), LocalDateTime.now(), 0)
                                    && !LocalDateTimeUtils.compareDate(s1.getCloseDate(), LocalDateTime.now(), 0))
                            )
                            .map(s1 -> s1.getCjRmbTotal().add(s1.getAuditDate() == null ? BigDecimal.ZERO : s1.getBcTotal()))
                            .reduce(BigDecimal.ZERO, BigDecimal::add);
                }));
        //获取标准业态回款金额Map
        Map<String, BigDecimal> proProductReturnAmountMap = contractGUIDListByProProductCodeMap.entrySet().stream()
                .collect(Collectors.toMap(Map.Entry::getKey, s -> {
                    List<String> contractGUIDList = s.getValue();
                    return receiptsAmountMap.entrySet().stream()
                            .filter(s1 -> contractGUIDList.contains(s1.getKey()))
                            .flatMap(s1->s1.getValue().entrySet().stream()
                                    //保留回款时间小于当月的数据
                                    .filter(s2-> compareYearMonth(s2.getKey(),LocalDateTimeUtils.getLocalMonth(LocalDateTime.now())) <0)
                                    .map(Map.Entry::getValue)
                            ).reduce(BigDecimal.ZERO, BigDecimal::add);
                }));

        Map<String, BigDecimal> proProductReturmRateMap = proProductContractAmountMap.entrySet().stream()
                .collect(Collectors.toMap(Map.Entry::getKey, s -> BigDecimalUtils.divBigDecimal(proProductReturnAmountMap.get(s.getKey()),s.getValue(), 5)));


        return proProductReturmRateMap;
    }

    /**
     * 比较两个年月大小
     * -1 小于
     * 0 等于
     * 1 大于
     * @param yearMontA yyyy-mm
     * @param yearMonthB yyyy-mm
     * @return
     */
    private int compareYearMonth(String yearMontA, String yearMonthB) {
        String[] splitA = yearMontA.split("-");
        String[] splitB = yearMonthB.split("-");

        int yearA = Integer.parseInt(splitA[0]);
        int monthA = Integer.parseInt(splitA[1]);
        int yearB = Integer.parseInt(splitB[0]);
        int monthB = Integer.parseInt(splitB[1]);

        int compare = Integer.compare(yearA, yearB);
        if(compare == 0){
            compare = Integer.compare(monthA, monthB);
        }
        return compare;
    }

}
